home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-11-29 | 11.7 KB | 490 lines | [TEXT/MPS ] |
- PROGRAM SecurityPatrol(OUTPUT);
- {-------------------------------------------}
- (*
- ©1988 by Steve Seaquist. All rights reserved.
- Used by permission. Use at your own risk.
- No warranty is expressed or implied.
-
- This Macintosh virus-detecting program was
- originally published and explained in the
- February 1989 issue of MacTutor magazine.
- Some aspects of its design are important to
- security, and it uses some unusual
- techniques, so please read the article.
- *)
- {-------------------------------------------}
- USES
- MemTypes,QuickDraw,OSIntf,ToolIntf,
- PackIntf,CodeSizeLimits,Globals,
- MainDlog,PasLibIntf,Patrol;
- {$R-}
-
- CONST
- kPreprocessSelf = TRUE;
- kAwaitVerification = FALSE;
-
- VAR
- gInitSecPatDone: BOOLEAN;
- gRptFileOpen: BOOLEAN;
- gScrDmpEnbPtr: Ptr;
- gScrDmpEnbSave: SignedByte;
- o: Text;
-
- PROCEDURE PreprocessSelf; FORWARD;
- PROCEDURE Wryte
- (pStr: Str255); FORWARD;
- PROCEDURE WryteChar
- (pChar: CHAR); FORWARD;
- PROCEDURE WryteEoln; FORWARD;
- PROCEDURE WryteLn
- (pStr: Str255); FORWARD;
- PROCEDURE WryteNbr
- (pNbr: LONGINT;
- pNbrDigits:INTEGER); FORWARD;
- PROCEDURE WryteType
- (pType: ResType); FORWARD;
-
- {-------------------------------------------}
- {$Z*}
- {-------------------------------------------}
- PROCEDURE ExitSecurityPatrol;
- BEGIN
- IF gOption[eTrace] THEN
- Trace('ExitSecurityPatrol');
- gScrDmpEnbPtr^ := gScrDmpEnbSave;
- IF gRptFileOpen THEN
- Close(o);
- ExitToShell;
- END;
- {-------------------------------------------}
- {$Z-}
- {-------------------------------------------}
- PROCEDURE InitSecurityPatrol;
- VAR
- sConfig: LONGINT;
- BEGIN
- MaxApplZone;
- MoreMasters; MoreMasters; MoreMasters;
- gInitSecPatDone := FALSE;
- gRptFileOpen := FALSE;
- gScrDmpEnbPtr := Ptr(kScrDmpEnb);
- gScrDmpEnbSave := gScrDmpEnbPtr^;
- gScrDmpEnbPtr^ := 0;
- Textbook(@thePort);
- Write ('SecurityPatrol is a Mac virus ');
- Write ('detector. ');
- TextFace([bold,extend]);
- WriteLn('Use at your own risk. ');
- TextFace([]);
- Write ('The Save As... dialog below is ');
- WriteLn('to save error reports.');
- PLFlush(OUTPUT);
- PauseBriefly;
-
- InitGlobals;
- gOption[eBeeps] := TRUE; {override…}
- gDisabled[eFgPrC] := TRUE; {…defaults}
- InitMainDlog;
- InitPatrols;
-
- sConfig := BAnd(ORD4(Ptr(kSPConfig)^),$F);
- IF (sConfig = useFree)
- OR (sConfig = useAsync) THEN
- SFPutFile
- (gSFPutPt,'Filename, or cancel to print',
- 'SecurityPatrol Report',NIL,gSFRep)
- ELSE
- SFPutFile
- (gSFPutPt,'Filename, or cancel to quit',
- 'SecurityPatrol Report',NIL,gSFRep);
- WITH gSFRep DO
- IF good THEN
- BEGIN
- gCurrWDRefNum := vRefNum;
- BuildDirname;
- ReWrite(o,CONCAT(gCurrDirname,fName));
- END
- ELSE
- IF (sConfig = useFree)
- OR (sConfig = useAsync) THEN
- ReWrite(o,'PRINTER:')
- ELSE
- BEGIN
- WriteLn('Run cancelled.');
- PLFlush(OUTPUT);
- PauseBriefly;
- ExitSecurityPatrol;
- END;
- gRptFileOpen := TRUE;
-
- Wryte ('This copy of Security Patrol 1.0 ');
- Wryte ('is being maintained by ');
- TextFace([bold,extend]);
- gPgmrname := '<<your name here>>';
- WryteLn(gPgmrname);
- TextFace([]);
- Wryte ('The following run was done on ');
- GetTime(gDateTimeRec);
- WITH gDateTimeRec DO
- BEGIN
- year := year mod 100;
- WryteNbr (month,2);
- WryteChar('/');
- WryteNbr (day, 2);
- WryteChar('/');
- WryteNbr (year, 2);
- Wryte (' at ');
- WryteNbr (hour, 2);
- WryteChar(':');
- IF minute < 10 THEN
- WryteChar('0');
- WryteNbr (minute,1);
- WryteLn ('.');
- END;
- IF kPreprocessSelf THEN
- PreprocessSelf
- ELSE
- WryteLn('Didn’t perform self-tests.');
- gInitSecPatDone := TRUE;
- END;
- {-------------------------------------------}
- PROCEDURE PreprocessSelf;
- VAR
- i: INTEGER;
- sC1Ptr: LONGINT;
- sEntActual: LONGINT;
- sEntShouldB:LONGINT;
- sOffActual: LONGINT;
- sOffShouldB:LONGINT;
- sResType: ResType;
- sSave: TMainOpt;
- {--------------------}
- PROCEDURE Abort
- (pStr: Str255);
- BEGIN
- ErrorBegins(pStr);
- WryteEoln;
- CommentBegins;
- WryteLn('Assuming infected.');
- CommentBegins;
- Wryte ('Contact ');
- Wryte (gPgmrname);
- WryteLn(' to be sure.');
- CommentBegins;
- Wryte ('(These msgs apply to ');
- Wryte (gCurrFilename);
- WryteLn(' itself, not to your system.)');
- CommentBegins;
- gOption[eAwait] := TRUE;
- gOption[eBeeps] := TRUE;
- ErrorEnds(4);
- ExitSecurityPatrol;
- END;
- {--------------------}
- BEGIN
- BlockMove(@gOption,@sSave,SIZEOF(TMainOpt));
- ZeroOut (@gOption, SIZEOF(TMainOpt));
- gOption[eRmVir] := TRUE;
- { gOption[eTrace] := TRUE; }
- IF gOption[eTrace] THEN
- Trace('PreprocessSelf');
- GetCodeSizeLimits;
- GetRsrc(@gCode0,'CODE',0,ResId);
- IF (gCode0.fFlag <> kRsrcHdlValid) THEN
- Abort('Unable to get own CODE 0');
- IF NOT(Code0IsValid) THEN
- Abort('Found unexpected CODE 0 header');
- LookForKnownViruses;
- FOR i := 1 TO Count1Types DO
- BEGIN
- Get1IndType(sResType,i);
- IF (sResType = 'SIZE') THEN
- BEGIN
- IF (Count1Resources('SIZE') > 1) THEN
- Abort('Too many SIZE resources');
- GetRsrc(@gCurrRsrc,'SIZE',1,Index);
- IF (gCurrRsrc.fSize > 10) THEN
- Abort('SIZE resource too large');
- ReleaseRsrc(@gCurrRsrc);
- END
- ELSE IF (sResType <> 'CODE') THEN
- BEGIN
- ErrorBegins('Found a rsrc of type ');
- WryteType(sResType);
- ErrorEnds(0);
- Abort ('Only CODE and SIZE allowed');
- END;
- END;
- WITH TJTHdl(gCode0.fHdl)^^,fJTEntry[1] DO
- BEGIN
- IF (fNbrBytesInTable <> gJTSize) THEN
- BEGIN
- ErrorBegins('Jump table size is ');
- WryteNbr (fNbrBytesInTable,1);
- Wryte (', should be ');
- WryteNbr (gJTSize,1);
- ErrorEnds(0);
- Abort ('Invalid Jump Table size');
- END;
- IF NOT(JTEIsValid(@fJTEntry[1])) THEN
- Abort('Invalid Jump Table entry');
- IF (fSegId <> 1) THEN
- BEGIN
- ErrorBegins('Enters at CODE ');
- WryteNbr(fSegId,1);
- Wryte (', should enter at CODE 1');
- ErrorEnds(0);
- Abort ('Invalid start address');
- END;
- sOffActual := 4 + fOffset;
- END;
- WITH gCurrRsrc DO
- BEGIN
- GetRsrc(@gCurrRsrc,'CODE',1,ResId);
- IF (fFlag <> kRsrcHdlValid) THEN
- Abort('Couldn’t look at own CODE 1');
- sC1Ptr := BAND($00FFFFFF,ORD4(fHdl^));
- sEntActual := sC1Ptr + sOffActual;
- IF (TWordPtr(sEntActual)^ = $4EFA) THEN
- BEGIN
- sOffActual :=
- sOffActual+2+TWordPtr(sEntActual+2)^;
- sEntActual := sC1Ptr + sOffActual;
- END;
- sEntShouldB := gEntryPoint;
- sOffShouldB := sEntShouldB - sC1Ptr;
- IF (sEntActual <> sEntShouldB) THEN
- BEGIN
- ErrorBegins('Invalid start address');
- WryteEoln;
- CommentBegins;
- Wryte ('Enters at address $');
- ShortHexDump(@sEntActual,4);
- Wryte (' (CODE 1 + $');
- ShortHexDump(@sOffActual,4);
- Wryte (') --> $');
- ShortHexDump(Ptr(sEntActual),4);
- WryteEoln;
- CommentBegins;
- Wryte ('Should enter at $');
- ShortHexDump(@sEntShouldB,4);
- Wryte (' (CODE 1 + $');
- ShortHexDump(@sOffShouldB,4);
- Wryte (') --> $');
- ShortHexDump(Ptr(sEntShouldB),4);
- WryteEoln;
- CommentBegins;
- Wryte ('CODE 1 begins at $');
- ShortHexDump(@sC1Ptr,4);
- ErrorEnds(0);
- Abort ('This is not a user error');
- END;
- ReleaseRsrc(@gCurrRsrc);
- END;
- ReleaseRsrc(@gCode0);
-
- IF (Count1Resources('CODE')>gMaxCode+1) THEN
- Abort('Too many CODE resources.');
- IF kAwaitVerification THEN
- BEGIN
- ErrorBegins('The following are the ');
- Wryte ('“fingerprints” of ');
- Wryte (gCurrFilename);
- Wryte (' itself:');
- ErrorEnds(0);
- END;
- FOR i := 0 TO gMaxCode DO
- WITH gCurrRsrc DO
- BEGIN
- GetRsrc(@gCurrRsrc,'CODE',i,ResId);
- IF (fFlag <> kRsrcHdlValid) THEN
- Abort('Couldn’t look at next CODE');
- IF (fSize > gSizeLimit[i]) THEN
- BEGIN
- ErrorBegins('Failed a CODE size test');
- WryteEoln;
- CommentRsrcBegins(@gCurrRsrc);
- Wryte (' size is ');
- WryteNbr(fSize,1);
- Wryte (', which exceeds its ');
- WryteNbr(gSizeLimit[i],1);
- WryteLn (' size limit.');
- Abort('May contain an imbedded virus');
- END;
- IF kAwaitVerification THEN
- BEGIN
- ProcessCurrRsrc;
- CommentFgPrRsrc(@gCurrRsrc);
- END;
- ReleaseRsrc(@gCurrRsrc);
- END;
- IF kAwaitVerification THEN
- BEGIN
- ErrorBegins('Time to make a decision:');
- WryteEoln;
- CommentBegins;
- WryteLn('Verify fingerprints if you can');
- CommentBegins;
- WryteLn('Press command-period to abort');
- CommentBegins;
- WryteLn('Press any other key to continue');
- CommentBegins;
- gOption[eAwait] := TRUE;
- ErrorEnds(0);
- IF gAbortPatrol THEN
- BEGIN
- PauseBriefly;
- ExitSecurityPatrol;
- END;
- END;
- WryteLn('Passed all current self-tests.');
- PauseBriefly;
- BlockMove(@sSave,@gOption,SIZEOF(TMainOpt));
- END;
- {-------------------------------------------}
- {$Z*}
- {-------------------------------------------}
- PROCEDURE WriteFilenameToReport;
- BEGIN
- WITH gReportFlags DO
- IF NOT(fWroteFilename) THEN
- BEGIN
- IF NOT(fWroteDirname) THEN
- BEGIN
- WriteLn(o,gCurrDirname);
- fWroteDirname := TRUE;
- END;
- Write(o,gInd,gCurrFilename);
- IF gActiveSelf THEN
- BEGIN
- Write(o,' (Active Self)');
- IF gInitSecPatDone
- AND NOT(kProcessSelf) THEN
- Write(o,' skipped');
- END
- ELSE IF gActiveSys THEN
- Write(o,' (Active System)');
- WriteLn(o);
- fWroteFilename := TRUE;
- END;
- END;
- {-------------------------------------------}
- PROCEDURE WriteFilenameToScreen;
- BEGIN
- WITH gScreenFlags DO
- IF NOT(fWroteFilename) THEN
- BEGIN
- IF NOT(fWroteDirname) THEN
- BEGIN
- WriteLn(gCurrDirname);
- fWroteDirname := TRUE;
- END;
- Write(gInd,gCurrFilename);
- IF gActiveSelf THEN
- BEGIN
- Write(' (Active Self)');
- IF gInitSecPatDone
- AND NOT(kProcessSelf) THEN
- Write(' skipped');
- END
- ELSE IF gActiveSys THEN
- Write(' (Active System)');
- WriteLn;
- PLFlush(OUTPUT);
- fWroteFilename := TRUE;
- END;
- END;
- {-------------------------------------------}
- PROCEDURE Wryte
- (pStr: Str255);
- BEGIN
- Write(pStr);
- IF gRptFileOpen THEN
- Write(o,pStr);
- END;
- {-------------------------------------------}
- PROCEDURE WryteChar
- (pChar: CHAR);
- BEGIN
- Write(pChar);
- IF gRptFileOpen THEN
- Write(o,pChar);
- END;
- {-------------------------------------------}
- PROCEDURE WryteEoln;
- BEGIN
- WriteLn;
- PLFlush(OUTPUT);
- IF gRptFileOpen THEN
- BEGIN
- WriteLn(o);
- { PLFlush(o); control w/option chk box? }
- END;
- END;
- {-------------------------------------------}
- PROCEDURE WryteFilename;
- BEGIN
- IF gRptFileOpen THEN
- WriteFilenameToReport;
- WriteFilenameToScreen;
- END;
- {-------------------------------------------}
- PROCEDURE WryteFilenameToScreenOnlyForNow;
- BEGIN
- WriteFilenameToScreen;
- END;
- {-------------------------------------------}
- PROCEDURE WryteLn
- (pStr: Str255);
- BEGIN
- WriteLn(pStr);
- PLFlush(OUTPUT);
- IF gRptFileOpen THEN
- BEGIN
- WriteLn(o,pStr);
- { PLFlush(o); control w/option chk box? }
- END;
- END;
- {-------------------------------------------}
- PROCEDURE WryteNbr
- (pNbr: LONGINT;
- pNbrDigits:INTEGER);
- BEGIN
- Write(pNbr:pNbrDigits);
- IF gRptFileOpen THEN
- Write(o,pNbr:pNbrDigits);
- END;
- {-------------------------------------------}
- PROCEDURE WryteType
- (pType: ResType);
- BEGIN
- Write(pType);
- IF gRptFileOpen THEN
- Write(o,pType);
- END;
- {-------------------------------------------}
- PROCEDURE zzSecurityPatrol;
- BEGIN
- END;
- {*******************************************}
- BEGIN
- InitSecurityPatrol;
- WHILE TRUE DO
- BEGIN
- gAbortPatrol := FALSE;
- CASE MainDlogWorkRequested OF
- eDirs: PatrolDirectories (FALSE);
- eDiry: PatrolDirectories (TRUE);
- eEvery: PatrolEverything;
- eFiles: PatrolFiles;
- OTHERWISE LEAVE;
- END; {CASE}
- END;
- WryteEoln;
- WryteLn('*******************************');
- WryteEoln;
- WryteLn ('Totals over all patrols:');
- ListCounts(@gTotals);
- ExitSecurityPatrol;
- {*******************************************}
- END.